package com.zhaoyd.redislock.core.lock;


import com.zhaoyd.redislock.core.BaseLogger;
import com.zhaoyd.redislock.core.SimpleRedisClient;
import org.apache.commons.lang3.StringUtils;

/**
 * 基于redis的分布式同步锁
 * @author zhaoyd
 * @date 2020-05-21
 */
public class SyncLock extends BaseLogger {
    private String key;
    private SimpleRedisClient simpleRedisClient;
    /**redis锁时间 ms*/
    private long expire;
    /**获取redis锁安全时间，默认expire*5 */
    private long safetyTime;
    /**每次获取锁等待时间*/
    private long waitMillisPer = 50L;

    /**
     * 有参构造函数
     * @param key 锁key
     * @param simpleRedisClient redisClient
     * @param expire 锁超时时间
     * @param safetyTime 安全时间
     */
    public SyncLock(String key, SimpleRedisClient simpleRedisClient, Long expire, Long safetyTime){
        this.key = key;
        this.simpleRedisClient = simpleRedisClient;
        this.expire = expire;
        if(safetyTime == null){
            //默认expire*5
            this.safetyTime = expire*5;
        }else {
            this.safetyTime = safetyTime;
        }
    }

    /**
     * 尝试获取锁（立即返回）
     * @return 是否获取成功
     */
    public boolean lockNow() {
        //获取成功时，加锁并返回true
        String value = Thread.currentThread().getId() + "-" + Thread.currentThread().getName();
        boolean locked = simpleRedisClient.setNX(key, value, expire/1000);
        return locked;
    }

    /**
     * 阻塞获取锁
     * @return 是否获取成功
     */
    public boolean lock() throws InterruptedException{
        String value = Thread.currentThread().getId() + "-" + Thread.currentThread().getName();
        log.info("lock start key={},value={}", key, value);
        //已等待毫秒数
        long waitMillisAlready = 0L;
        //最大等待毫秒数
        long maxWaitMillis = this.safetyTime;
        while (waitMillisAlready < maxWaitMillis) {
            boolean locked = simpleRedisClient.setNX(key, value, expire/1000);
            if(locked){
                //成功获取锁
                log.info("lock success key={},value={}", key, value);
                return true;
            }else{
                Thread.sleep(waitMillisPer);
                waitMillisAlready += waitMillisPer;
            }
        }
        //超过safetyTime仍然未获取到锁，强制获取锁
        log.info("lock fail key={},value={}, try force lock", key, value);
        simpleRedisClient.set(key, value, expire/1000);
        return true;
    }

    /**
     * 释放锁， 只释放自己的
     */
    public void unLock() {
        String value = Thread.currentThread().getId() + "-" + Thread.currentThread().getName();
        String redisValue = simpleRedisClient.get(key);
        if(redisValue == null){
            //已经失效了
            return;
        }
        if (StringUtils.equals(redisValue, value)) {
            simpleRedisClient.del(key);
            log.info("unlock key={},value={}", key, value);
        }else{
            //不释放其他线程的锁
            log.info("unlock fail key={},value={},redisValue={}", key, value,redisValue);
        }
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public SimpleRedisClient getSimpleRedisClient() {
        return simpleRedisClient;
    }

    public void setSimpleRedisClient(SimpleRedisClient simpleRedisClient) {
        this.simpleRedisClient = simpleRedisClient;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    public long getSafetyTime() {
        return safetyTime;
    }

    public void setSafetyTime(long safetyTime) {
        this.safetyTime = safetyTime;
    }
}
